/************************************************************
 * Copyright (C), 2022-2030, dina. Co., Ltd.
 * FileName:        cycle_buffer.h
 * Author:          Dan Yin
 * Date:            2022-11-23
 * Description:     the class of buffer for serialport
 * Version:         1.0.0.0
 **********************************************************/

#ifndef CYCLE_BUFFER_H
#define CYCLE_BUFFER_H

#include <stddef.h>
#include <stdlib.h>

#ifndef DEF_BUF_MIN
#define _min_(a,b) (((a) < (b)) ? (a) : (b))
#endif

#ifndef DEF_BUF_MAX
#define _max_(a,b) (((a) > (b)) ? (a) : (b))
#endif

#define DF_MIN_BUF_SIZE     4096
#define DF_MAX_BUF_SIZE     8192

template<class _value_type >
class CycleBuffer
{
public:
    CycleBuffer(int buf_size = DF_MAX_BUF_SIZE);
    ~CycleBuffer();

    void clear(){ _r = 0; _w = 0;}
    unsigned int capacity() const { return _buffer_size; }
    unsigned int available() { return _buffer_size - (_w - _r); }
    unsigned int size()  { return  _w - _r; }
    bool full() { return (size() == _buffer_size) ? true : false; }
    bool empty() { return (size() == 0) ? true : false; }

    unsigned int write(_value_type* buffer, unsigned int len);
    unsigned int read(_value_type* buffer, unsigned int len);

    bool pop_front(_value_type& value);
    bool pop_front(void);
    bool push_back(_value_type& value);
    bool push_front(_value_type& value);
    bool resize(int newSize);
    _value_type& front(void) const { return _buffer[_r % _buffer_size]; }
    _value_type& operator[](size_t id) { return _buffer[(_r + id) % _buffer_size]; }

protected:
   inline unsigned int _max(unsigned int a, unsigned int b) { return _max_(a, b); }
   inline unsigned int _min(unsigned int a, unsigned int b) { return _min_(a, b); }

private:
   int _buffer_size;
   _value_type * _buffer;
   volatile unsigned int _r;
   volatile unsigned int _w;
};

template<class _value_type >
CycleBuffer<_value_type >::CycleBuffer(int buf_size)
      : _buffer_size(buf_size), _buffer(NULL), _r(0), _w(0)
{
    if(DF_MIN_BUF_SIZE > _buffer_size) _buffer_size = DF_MIN_BUF_SIZE;
    _buffer = new _value_type[_buffer_size];
}

template<class _value_type >
CycleBuffer<_value_type >::~CycleBuffer()
{
     if (_buffer)  delete[] _buffer;
      _buffer = NULL;
}

template<class _value_type >
unsigned int CycleBuffer<_value_type >::write(_value_type* buffer, unsigned int len)
{
    unsigned int l,offset;
    offset = _w % _buffer_size;
    len = _min(len, _buffer_size - abs((int)(_w - _r)));

    // first put the data starting from fifo->in to buffer end
    l = _min(len, _buffer_size - offset );
    memcpy(_buffer + offset , buffer, l*sizeof(_value_type));

    // then put the rest (if any) at the beginning of the buffer
    memcpy(_buffer, buffer + l, (len - l)*sizeof(_value_type));

    _w += len;
    return len;
}

template<class _value_type >
 unsigned int CycleBuffer<_value_type >::read(_value_type* buffer, unsigned int len)
{
    unsigned int l,offset;
    offset = _r % _buffer_size;
    len = _min(len, abs((int)(_w - _r)));

    // first get the data from fifo->out until the end of the buffer
    l = _min(len, _buffer_size - offset);
    memcpy(buffer, _buffer + offset, l*sizeof(_value_type));

    // then get the rest (if any) from the beginning of the buffer
    memcpy(buffer + l, _buffer, (len - l)*sizeof(_value_type));
    _r += len;

    return len;
}

template<class _value_type >
bool CycleBuffer<_value_type >::pop_front(_value_type& value)
{
    if(empty()) {
        return false;
    }
    else {
        value = _buffer[_r++ % _buffer_size];
        return true;
    }
}

template<class _value_type >
bool CycleBuffer<_value_type >::pop_front(void)
{
    if(empty()) {
        return false;
    }
    else {
        _r++;
        return true;
    }
}

template<class _value_type >
bool CycleBuffer<_value_type >::push_back(_value_type& value)
{
    if(full()) {
        return false;
    }
    else {
        _buffer[_w++ % _buffer_size] = value;
        return true;
    }
}

template<class _value_type >
bool CycleBuffer<_value_type >::push_front(_value_type& value)
{
    if(full()) {
        return false;
    }
    else {
        _buffer[_r-- % _buffer_size] = value;
        return true;
    }
}

template<class _value_type >
bool CycleBuffer<_value_type >::resize(int new_size)
{
    size_t old_size = size();

    if( (old_size > new_size) || (DF_MIN_BUF_SIZE > new_size)) { return false; }

    _value_type *new_value_ptr = new _value_type[new_size];
    _buffer_size = new_size;
     if(NULL != _buffer) {
        if(0 < old_size) read(new_value_ptr,old_size);
        delete[] _buffer;
        _buffer = NULL;
    }

    _r = 0;
    _w = old_size;
    _buffer = new_value_ptr;

    return true;
}

#endif // CYCLE_BUFFER_H
